﻿/*!
 * @file CondLock.h
 *
 * @author  shikh
 * @date    2015/10/23
 * @brief   条件锁，其中timedwait可以替代sleep函数
 *
 */
#pragma once

#include <stdio.h>
#include <errno.h>

#ifdef WIN32
  #include <Windows.h>
#else
  #include <sys/time.h>
  #include <unistd.h>
  #include <pthread.h>
#endif

namespace lock{

    /*! 定义元本地锁 */
#if _WIN32||_WIN64
    typedef HANDLE              native_cond_type; 
    typedef CRITICAL_SECTION    native_mutex_type;
#else
    typedef pthread_cond_t      native_cond_type; 
    typedef pthread_mutex_t     native_mutex_type;
#endif // _WIN32||_WIN64

    //! Base class for types that should not be assigned.
    //! Base class for types that should not be copied or assigned.
    class CNoCopyable {
    private:
        // Deny assignment
        void operator=( const CNoCopyable& );
        //! Deny copy construction
        CNoCopyable( const CNoCopyable& );

    protected:
        //! Allow default construction
        CNoCopyable(){}

    public:
        virtual ~CNoCopyable() {}
    };

    /*!
     * @class CMutexLock
     *
     * @brief 互斥锁，用CriticalSection/pthread_mutex实现
     *
     */
    class CMutexLock : CNoCopyable {
    public:
        CMutexLock() {
        #if _WIN32||_WIN64
            InitializeCriticalSection(&impl);
        #else
            pthread_mutex_init(&impl, NULL);
        #endif
        }

        virtual ~CMutexLock() {
        #if _WIN32||_WIN64
            DeleteCriticalSection(&impl);
        #else
            pthread_mutex_destroy(&impl); 

        #endif 
        }

    public:
        /*!
         * @brief   Acquire lock
         * @return  void
         */
        void Lock() {
        #if _WIN32||_WIN64
            EnterCriticalSection(&impl);
        #else
            pthread_mutex_lock(&impl);
        #endif 
        }

        /*!
         * @brief   Try acquiring lock (non-blocking)
         * @return  bool (Return true if lock acquired; false otherwise.)
         */
        bool TryLock() {
        #if _WIN32||_WIN64
            return TryEnterCriticalSection(&impl)!=0;
        #else
            return pthread_mutex_trylock(&impl)!= EBUSY;
        #endif 
        }

        /*!
         * @brief   Release lock
         * @return  void
         */
        void Unlock() {
        #if _WIN32||_WIN64
            LeaveCriticalSection(&impl);
        #else
            pthread_mutex_unlock(&impl);
        #endif 
        }

    public:
        native_mutex_type impl;
    };

    /*!
     * @class CAutoReleaseLock
     *
     * @brief 自动释放互斥锁模板
     *
     */
    template<class T>
    class CAutoReleaseLock
    {
    public:
        CAutoReleaseLock(T& lock)
            :m_lock(lock)
        {
            m_lock.Lock();
        }

        ~CAutoReleaseLock()
        {
            m_lock.Unlock();
        }

        void Lock() {
            m_lock.Lock();
        }
        bool TryLock() {
            return m_lock.TryLock();
        }
        void Unlock() {
            return m_lock.Unlock();
        }
    private:
        T& m_lock;
    };

    /*!
     * @class CCondLock
     *
     * @brief 条件锁
     *
     */
    class CCondLock : public CNoCopyable
    {
    public:
        enum 
        {
            RES_FAILED  = -1,
            RES_OK      = 0,
            RES_TIMEOUT = 1,
        };
    public:
        CCondLock()
            :m_is_opened(false)
        #if _WIN32||_WIN64
            ,m_cond(NULL)
        #endif
        {
            Open();
        }

        virtual ~CCondLock()
        {
            Close();
        #if _WIN32||_WIN64
            m_cond = NULL;
        #endif
        }

    public:
        /*!
         * @brief   无限等待
         * @return  int (RES_FAILED/RES_OK)
         */
        virtual int Wait()
        {
        #if _WIN32 || _WIN64
        #else
            CAutoReleaseLock<CMutexLock> lock(m_lock);
        #endif
            return WaitImpl();
        }

        /*!
         * @brief   限时等待
         * @param   ms  (毫秒)
         * @return  int (RES_FAILED/RES_OK/RES_TIMEOUT)
         */
        virtual int TimedWait(unsigned long ms)
        {
        #if _WIN32 || _WIN64
        #else
            CAutoReleaseLock<CMutexLock> lock(m_lock);
        #endif
            return TimedWaitImpl(ms);
        }

        /*!
         * @brief   发送信号
         * @return  bool
         */
        virtual bool Signal()
        {
            CAutoReleaseLock<CMutexLock> lock(m_lock);
            return SignalImpl();
        }

        /*!
         * @brief   重置信号
         * @return  bool
         */
        virtual bool Reset()
        {
            return true;
        }


        /*!
         * @brief   打开
         * @return  bool
         */
        virtual bool Open()
        {
            CAutoReleaseLock<CMutexLock> lock(m_lock);
            return OpenImpl();
        }

        /*!
         * @brief   关闭
         * @return  bool
         */
        virtual bool Close()
        {
            CAutoReleaseLock<CMutexLock> lock(m_lock);
            return CloseImpl();
        }

    protected:
        /*!
         * @brief   无限等待，无锁实现
         * @return  int (RES_FAILED/RES_OK/RES_TIMEOUT)
         */
        int WaitImpl()
        {
            int ret;
        #if _WIN32||_WIN64
            DWORD result = WaitForSingleObject(m_cond, -1);

            if (WAIT_TIMEOUT == result) ret = RES_TIMEOUT;
            else if (WAIT_OBJECT_0 == result) ret = RES_OK;
            else ret = RES_FAILED;
        #else
            int result = pthread_cond_wait(&m_cond, &(m_lock.impl));

            if (ETIMEDOUT == result) ret = RES_TIMEOUT;
            else if (EINTR == result) ret = RES_OK;
            else ret = RES_FAILED;
        #endif
            return ret;
        }

        /*!
         * @brief   限时等待，无锁实现
         * @param   ms  (毫秒)
         * @return  int (RES_FAILED/RES_OK/RES_TIMEOUT)
         */
        int TimedWaitImpl(unsigned long ms)
        {
            int ret;
        #if _WIN32||_WIN64
            DWORD result = WaitForSingleObject(m_cond, ms);

            if (WAIT_TIMEOUT == result) ret = RES_TIMEOUT;
            else if (WAIT_OBJECT_0 == result) ret = RES_OK;
            else ret = RES_FAILED;
        #else
            struct timespec outtime;
            outtime.tv_sec = time(NULL) + ms/1000;
            outtime.tv_nsec = (ms%1000)*1000000;

            int result = pthread_cond_timedwait(&m_cond, &(m_lock.impl), &outtime);

            if (ETIMEDOUT == result) ret = RES_TIMEOUT;
            else if (EINTR == result) ret = RES_OK;
            else ret = RES_FAILED;
        #endif
            return ret;
        }

        /*!
         * @brief   发送信号，无锁实现
         * @return  bool
         */
        bool SignalImpl()
        {
            if (!m_is_opened) {
                return false;
            }
        #if _WIN32||_WIN64
            if (m_cond) SetEvent(m_cond);
            else {
                return false;
            }
        #else
            pthread_cond_signal(&m_cond);
        #endif
            return true;
        }

        /*!
         * @brief   打开，无锁实现
         * @return  bool
         */
        bool OpenImpl()
        {
            if (m_is_opened) {
                return true;
            }
        #if _WIN32||_WIN64
            //设置为自动模式
            m_cond = CreateEvent(NULL,FALSE,FALSE,NULL);
            if (NULL == m_cond) {
                return false;
            }
        #else
            int result = pthread_cond_init(&m_cond, NULL);
            if (0 != result) return false;
        #endif
            m_is_opened = true;
            return true;
        }

        /*!
         * @brief   关闭，无锁实现
         * @return  bool
         */
        bool CloseImpl()
        {
            if (!m_is_opened) {
                return true;
            }
        #if _WIN32||_WIN64
            if (m_cond) SetEvent(m_cond);
            if (m_cond) CloseHandle(m_cond);
        #else
            // cond broadcast
            pthread_cond_broadcast(&m_cond);

            // destroy
            pthread_cond_destroy(&m_cond);
        #endif
            m_is_opened = false;
            return true;
        }

    private:
        native_cond_type m_cond;
        CMutexLock m_lock;
        bool m_is_opened;
    };
}